package com.youdianstar.app.common.util

/**
 * 坐标转换工具类
 * 用于在不同坐标系之间进行转换
 * 
 * 坐标系说明：
 * - WGS84: GPS原始坐标系（国际标准）
 * - GCJ02: 火星坐标系（中国标准，Google Maps、高德地图使用）
 * - BD09: 百度坐标系（百度地图使用）
 */
object CoordinateConverter {
    
    /**
     * 坐标数据类
     */
    data class Coordinate(val latitude: Double, val longitude: Double)
    
    // 圆周率
    private const val PI = 3.1415926535897932384626
    // 长半轴
    private const val AXIS = 6378245.0
    // 偏心率
    private const val OFFSET = 0.00669342162296594323
    
    /**
     * BD09坐标转GCJ02坐标（火星坐标系）
     * 后端返回的坐标通常是BD09，需要转换为GCJ02才能在Google Maps上正确显示
     * 
     * @param bdLat BD09纬度
     * @param bdLng BD09经度
     * @return GCJ02坐标
     */
    fun bd09ToGcj02(bdLat: Double, bdLng: Double): Coordinate {
        val x = bdLng - 0.0065
        val y = bdLat - 0.006
        val z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * PI)
        val theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * PI)
        val gcjLng = z * Math.cos(theta)
        val gcjLat = z * Math.sin(theta)
        return Coordinate(gcjLat, gcjLng)
    }
    
    /**
     * GCJ02坐标转BD09坐标
     * 
     * @param gcjLat GCJ02纬度
     * @param gcjLng GCJ02经度
     * @return BD09坐标
     */
    fun gcj02ToBd09(gcjLat: Double, gcjLng: Double): Coordinate {
        val z = Math.sqrt(gcjLng * gcjLng + gcjLat * gcjLat) + 0.00002 * Math.sin(gcjLat * PI)
        val theta = Math.atan2(gcjLat, gcjLng) + 0.000003 * Math.cos(gcjLng * PI)
        val bdLng = z * Math.cos(theta) + 0.0065
        val bdLat = z * Math.sin(theta) + 0.006
        return Coordinate(bdLat, bdLng)
    }
    
    /**
     * WGS84坐标转GCJ02坐标
     * 
     * @param wgsLat WGS84纬度
     * @param wgsLng WGS84经度
     * @return GCJ02坐标
     */
    fun wgs84ToGcj02(wgsLat: Double, wgsLng: Double): Coordinate {
        var dLat = transformLat(wgsLng - 105.0, wgsLat - 35.0)
        var dLng = transformLng(wgsLng - 105.0, wgsLat - 35.0)
        val radLat = wgsLat / 180.0 * PI
        var magic = Math.sin(radLat)
        magic = 1 - OFFSET * magic * magic
        val sqrtMagic = Math.sqrt(magic)
        dLat = (dLat * 180.0) / ((AXIS * (1 - OFFSET)) / (magic * sqrtMagic) * PI)
        dLng = (dLng * 180.0) / (AXIS / sqrtMagic * Math.cos(radLat) * PI)
        val gcjLat = wgsLat + dLat
        val gcjLng = wgsLng + dLng
        return Coordinate(gcjLat, gcjLng)
    }
    
    /**
     * GCJ02坐标转WGS84坐标
     * 
     * @param gcjLat GCJ02纬度
     * @param gcjLng GCJ02经度
     * @return WGS84坐标
     */
    fun gcj02ToWgs84(gcjLat: Double, gcjLng: Double): Coordinate {
        var dLat = transformLat(gcjLng - 105.0, gcjLat - 35.0)
        var dLng = transformLng(gcjLng - 105.0, gcjLat - 35.0)
        val radLat = gcjLat / 180.0 * PI
        var magic = Math.sin(radLat)
        magic = 1 - OFFSET * magic * magic
        val sqrtMagic = Math.sqrt(magic)
        dLat = (dLat * 180.0) / ((AXIS * (1 - OFFSET)) / (magic * sqrtMagic) * PI)
        dLng = (dLng * 180.0) / (AXIS / sqrtMagic * Math.cos(radLat) * PI)
        val wgsLat = gcjLat - dLat
        val wgsLng = gcjLng - dLng
        return Coordinate(wgsLat, wgsLng)
    }
    
    /**
     * 纬度转换
     */
    private fun transformLat(lng: Double, lat: Double): Double {
        var ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * lat + 0.1 * lng * lat + 0.2 * Math.sqrt(Math.abs(lng))
        ret += (20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0 / 3.0
        ret += (20.0 * Math.sin(lat * PI) + 40.0 * Math.sin(lat / 3.0 * PI)) * 2.0 / 3.0
        ret += (160.0 * Math.sin(lat / 12.0 * PI) + 320 * Math.sin(lat * PI / 30.0)) * 2.0 / 3.0
        return ret
    }
    
    /**
     * 经度转换
     */
    private fun transformLng(lng: Double, lat: Double): Double {
        var ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + 0.1 * lng * lat + 0.1 * Math.sqrt(Math.abs(lng))
        ret += (20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0 / 3.0
        ret += (20.0 * Math.sin(lng * PI) + 40.0 * Math.sin(lng / 3.0 * PI)) * 2.0 / 3.0
        ret += (150.0 * Math.sin(lng / 12.0 * PI) + 300.0 * Math.sin(lng / 30.0 * PI)) * 2.0 / 3.0
        return ret
    }
}

